create or replace type body m4o_context as

  -- ===========================================================================
  constructor function m4o_context(
    self in out nocopy m4o_context,
    i_name    in varchar2)
  return self as result
  is
  begin
    self.v_name := i_name;
    return;
  end;
  -- ===========================================================================
  member procedure do(
    self           in out nocopy m4o_context,
    i_process_step in varchar2)
  is
  begin
    self.v_process_step := i_process_step;
    m4o_dbg.p(i_process_step);
  end;
  -- ===========================================================================
  member procedure log(
    self     in out nocopy m4o_context,
    i_str    in varchar2)
  is
  begin
    if m4o_dbg.p_level > 1 then
      m4o_dbg.p(i_str);
    end if;
  end;
  -- ===========================================================================
  member procedure log_rowcount
  is
  begin
    m4o_dbg.p(sql%rowcount||' records.');
  end;
  -- ===========================================================================
  member procedure error(i_error_text in varchar2 := null)
  is
  begin
    m4o_dbg.p(rpad('##### ERROR CALL STACK ',80,'#'));
    m4o_dbg.p('(Unit Name='||
              self.v_name||
              ', Step='||
              self.v_process_step||
              ')');
    m4o_dbg.p(dbms_utility.format_call_stack);
    m4o_dbg.p(rpad('#',80,'#'));
    raise_application_error(-20001,
                            nvl(i_error_text,
                                'Assertion violated'));
  end;
  -- ===========================================================================
  member procedure assert(
    self         in out nocopy m4o_context,
    i_condition  in boolean,
    i_error_text in varchar2 := null)
  is
  begin
    if i_condition then
      null;
    else
      self.error(i_error_text);
    end if;
  end;
  -- ===========================================================================
  -- ensure that i_value is not null, otherwise raise error.
  member procedure required(
    self        in out nocopy m4o_context,
    i_var_value in varchar2,
    i_var_name  in varchar2)
  is
  begin
    if i_var_value is not null then
      null;
    else
      self.error('"'||i_var_name||'" is null');
    end if;
  end;
  -- ===========================================================================
  member procedure assert_one_row_changed(
    self         in out nocopy m4o_context,
    i_error_text in varchar2 := null)
  is
    v_rows pls_integer := sql%rowcount;
  begin
    if v_rows=1 then
      null;
    else
      self.error(nvl(i_error_text,
                     v_rows||' instead of 1 line processed'));
    end if;
  end;
end;
/
